/*
 * Url.java
 * 
 * This file is part of Netuno Proteu.
 * 
 * CajuScript is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3, or (at your option) 
 * any later version.
 * 
 * CajuScript is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with CajuScript.  If not, see <http://www.gnu.org/licenses/>.
*/

package org.netuno.proteu;

import java.io.File;
import java.io.IOException;
import org.apache.log4j.Logger;
import org.netuno.psamata.script.ScriptRunner;

import org.apache.commons.lang3.StringEscapeUtils;

/**
 * Url management.
 * @author eduveks
 */
public class Url {
    static Logger logger = Logger.getLogger(Url.class);
    public static final String SCRIPT_URL = "netuno_proteu_url";
    /**
     * Url filter and control.
     * @param proteu Proteu
     */
    public static void build(Proteu proteu) throws ProteuException {
        String url = proteu.getRequestHead().getString("Url");
        logger.info("Url: "+ url +" loading.");
        url = url.replace("\\", "/");
        while (true) {
            if (url.indexOf("./") != -1) {
                url = url.replace("./", "/");
            } else if (url.indexOf("//") != -1) {
                url = url.replace("//", "/");
            } else {
                break;
            }
        }
        try {
            String proteuUrlPath = ScriptRunner.searchFile(Config.getBase() + File.separator + SCRIPT_URL);
            try {
                if (proteuUrlPath != null) {
                    ScriptRunner scriptRunner = proteu.getScriptRunner();
                    scriptRunner.getBindings().put("proteu", proteu);
                    scriptRunner.getBindings().put("proteu_url", url);
                    scriptRunner.runFile(proteuUrlPath);
                    url = scriptRunner.getEngine().get("proteu_url").toString();
                    logger.info("Script \""+ SCRIPT_URL +"\" executed: " + proteuUrlPath);
                }
            } catch (Exception e) {
                logger.info("Script \""+ SCRIPT_URL +"\" error:" + proteuUrlPath);
                throw new ProteuException("Script \""+ SCRIPT_URL +"\" error: " + proteuUrlPath, e);
            }
            if (url.equals("")) {
                return;
            }
            File file = new File(Config.getPublic() + url);
            File fileBin = new File(Config.getBuild() + url);
            String filePath = Config.getPublic() + url;
            String fileBinPath = Config.getBuild() + url;
            if (!url.endsWith("/") && new File(filePath).isDirectory()) {
                filePath += "/";
            }
            File _fileProteu = null;
            String _fileProteuScriptPath = "";
            for (String key : Config.getExtensions().keySet()) {
                _fileProteu = new File(filePath);
                if (_fileProteu.exists() && filePath.endsWith(key)) {
                    _fileProteuScriptPath = Config.getBuild() + url.substring(0,url.lastIndexOf(key));
                    break;
                } else {
                    _fileProteu = null;
                }
            }
            File _fileProteuScript = null;
            for (String key : Config.getExtensions().keySet()) {
                _fileProteuScript = new File(filePath);
                if (_fileProteuScript.exists() && filePath.endsWith(Config.getExtensions().get(key))) {
                    break;
                } else {
                    _fileProteuScript = null;
                }
            }
            File _fileScript = null;
            String _fileScriptPath = ScriptRunner.searchFile(fileBinPath);
            if (_fileScriptPath != null) {
                _fileScript = new File(_fileScriptPath);
                if (!_fileScript.exists()) {
                    _fileScript = null;
                }
            }
            if (file.isDirectory()) {
                File fileJava = new File(filePath + "Index.java");
                File fileClass = new File(fileBinPath + "Index.class");
                File fileProteu = null;
                for (String key : Config.getExtensions().keySet()) {
                    fileProteu = new File(filePath + "index" + key);
                    if (fileProteu.exists()) {
                        break;
                    } else {
                        fileProteu = null;
                    }
                }
                File fileScript = new File(fileBinPath + "index.lua");
                if (fileJava.exists()) {
                    logger.info("Java: "+ fileJava.toString());
                    Compile.engineClass(proteu.getOutput(), fileJava, fileClass);
                    new Run(proteu, url.substring(1, url.length()) + "Index");
                } else if (fileClass.exists()) {
                    logger.info("Class: "+ fileClass.toString());
                    new Run(proteu, url.substring(1, url.length()) + "Index");
                } else if (fileProteu != null && fileProteu.exists()) {
                    logger.info("Proteu file: "+ fileProteu.toString());
                    new Compile(proteu.getOutput(), fileProteu, fileBinPath + "index");
                    new Run(proteu, fileBinPath + "index");
                } else if (fileScript != null && fileScript.exists()) {
                    logger.info("Script: "+ fileScript.toString());
                    new Run(proteu, fileScript.toString());
                } else {
                    logger.warn("Not found: "+ url);
                    new Error404(proteu);
                }
                fileJava = null;
                fileClass = null;
                fileProteu = null;
                fileScript = null;
            } else if (file.isFile() && !url.toLowerCase().endsWith(".class") && !url.toLowerCase().endsWith(".java") && !url.toLowerCase().endsWith(Config.getClassExtension()) && _fileScript == null && _fileProteu == null) {
            	logger.info("Download: "+ url);
            	new Download(proteu, file);
            } else {
                File fileJava = new File(file.toString() + ".java");
                File fileClass = new File(fileBin.toString() + ".class");
                File fileProteu = _fileProteu;
                File fileScript = _fileProteuScript;
                if (fileJava.exists()) {
                    logger.info("Java: "+ fileJava.toString());
                    Compile.engineClass(proteu.getOutput(), fileJava, fileClass);
                    new Run(proteu, url.substring(1, url.length()));
                } else if (fileClass.exists()) {
                    logger.info("Class: "+ fileClass.toString());
                    new Run(proteu, url.substring(1, url.length()));
                } else if (fileProteu != null && fileProteu.exists()) {
                    logger.info("Proteu file: "+ fileProteu.toString());
                    new Compile(proteu.getOutput(), fileProteu, _fileProteuScriptPath);
                    new Run(proteu, _fileProteuScriptPath);
                } else if (fileScript != null && fileScript.exists()) {
                    logger.info("Script: "+ fileScript.toString());
                    new Run(proteu, fileScript.toString());
                } else {
                    if (url.endsWith("/")) {
                        new Run(proteu, url + "Index");
                    } else if (url.endsWith(Config.getClassExtension())) {
                        new Run(proteu, url.substring(0, url.length() - Config.getClassExtension().length()));
                    } else {
                        logger.warn("Not found: "+ url);
                        new Error404(proteu);
                    }
                }
                fileJava = null;
                fileClass = null;
                fileProteu = null;
                fileScript = null;
            }
            file = null;
            fileBin = null;
        } catch (Throwable t) {
            logger.warn("Url error: "+ url, t);
            throwable(proteu, t, url);
            throw new ProteuException(t.getMessage(), t);
        } finally {
            proteu = null;
        }
    }

    private static void throwable(Proteu proteu, Throwable t, String url) {
        try {
        	if (!proteu.getConfig().getBoolean("proteu_silent_output_error")) {
	            if (Config.isErrorDetail()) {
	                proteu.getOutput().println();
	                proteu.getOutput().print("<div><b>");
	                proteu.getOutput().print("<span style=\"color: #ff0000;\">");
	                proteu.getOutput().print("Netuno Proteu Error");
	                proteu.getOutput().print(":</span> ");
	                proteu.getOutput().print(t.getMessage());
	                proteu.getOutput().println("</b></div>");
	                proteu.getOutput().println(getHtmlThrowable(proteu, t.getCause(), "", false));
				} else {
	                proteu.getOutput().println();
	                proteu.getOutput().print("<div><b>");
	                proteu.getOutput().print("<span style=\"color: #ff0000;\">");
	                proteu.getOutput().print("Netuno Proteu Error");
	                proteu.getOutput().print(":</span> ");
	                proteu.getOutput().print("See log for more details.");
	                proteu.getOutput().println("</b></div>");
	            }
        	}
        } catch (IOException e) {
            logger.warn("Printing throwable", e);
        }
    	if (Config.getErrorSendMail() != null) {
            try {
            	Throwable realThrowable = t;
            	if (realThrowable.getCause() != null && realThrowable.toString().startsWith("org.netuno.proteu.ProteuException")) {
            		realThrowable = realThrowable.getCause();
            		if (realThrowable.getCause() != null && realThrowable.toString().startsWith("java.lang.reflect.InvocationTargetException")) {
                		realThrowable = realThrowable.getCause();
                	}
            	}
            	Config.getErrorSendMail().setHtml(true);
        		String content = "<html>";
        		content = content.concat("<body>");
        		content = content.concat("<p><h2>").concat(url).concat("</h2></p>");
        		content = content.concat("<p><h3 style=\"color: red;\">").concat(StringEscapeUtils.escapeHtml4(realThrowable.toString())).concat("</h3></p>");
        		content = content.concat("<b>SESSION</b>");
        		content = content.concat("<pre>");
        		content = content.concat(StringEscapeUtils.escapeHtml4(proteu.getSession().getInFormat("\r\n", " = ")));
        		content = content.concat("</pre>");
        		content = content.concat("<b>REQUEST ALL</b>");
        		content = content.concat("<pre>");
        		content = content.concat(StringEscapeUtils.escapeHtml4(proteu.getRequestAll().getInFormat("\r\n", " = ")));
        		content = content.concat("</pre>");
        		content = content.concat(getHtmlThrowable(proteu, realThrowable, "", true));
        		content = content.concat("</body>");
        		content = content.concat("</html>");
        		Config.getErrorSendMail().setText(content);
        		Config.getErrorSendMail().sendMail();
            } catch (Exception e) {
                logger.warn("Can't send error mail", e);
            }
    	}
    }

    private static String getHtmlThrowable(Proteu proteu, Throwable t, String content, boolean withStackTrace) throws IOException {
        if (t != null) {
        	content = content.concat("\r\n");
        	content = content.concat("<div>");
        	content = content.concat("\r\n");
        	content = content.concat("<b>");
        	content = content.concat(StringEscapeUtils.escapeHtml4(withStackTrace ? t.toString() : t.getMessage() != null ? t.getMessage() : ""));
        	content = content.concat("</b>");
        	content = content.concat("\r\n");
        	if (withStackTrace) {
            	content = content.concat("<ul>");
            	content = content.concat("\r\n");
	        	for (StackTraceElement stackTraceElement : t.getStackTrace()) {
	        		if (stackTraceElement.getClassName().startsWith("javax.servlet")) {
	        			break;
	        		}
	        		content = content.concat("<li>").concat(StringEscapeUtils.escapeHtml4(stackTraceElement.toString())).concat("</li>");
	        		content = content.concat("\r\n");
	        	}
	        	content = content.concat("</ul>");
	        	content = content.concat("\r\n");
        	}
        	content = content.concat("</div>");
        	content = content.concat("\r\n");
            return getHtmlThrowable(proteu, t.getCause(), content, withStackTrace);
        }
        return content;
    }
}
